package org.Yiran.timetale_re.entity;

import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientGamePacketListener;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.EntityDataSerializers;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.entity.IEntityAdditionalSpawnData;
import net.minecraftforge.network.NetworkHooks;
import org.jetbrains.annotations.NotNull;

/**
 * 坐姿实体类
 * <p>
 * 该类表示一个用于实现玩家坐在椅子上的功能实体。
 * 具有以下功能：
 * 1. 管理玩家坐在椅子上的状态
 * 2. 检查椅子方块是否存在
 * 3. 处理玩家起身逻辑
 * 4. 限制玩家视角旋转范围
 * 5. 计算玩家起身时的位置
 * </p>
 */
public class SitEntity extends Entity implements IEntityAdditionalSpawnData {
    
    // ============================== 实体数据访问器 ==============================
    
    private static final EntityDataAccessor<Integer> DATA_FORWARD_DIRECTION = SynchedEntityData.defineId(SitEntity.class, EntityDataSerializers.INT);
    private static final EntityDataAccessor<Long> DATA_CHAIR_POS = SynchedEntityData.defineId(SitEntity.class, EntityDataSerializers.LONG);

    // ============================== 构造方法 ==============================
    
    /**
     * 构造函数 - 用于实体注册
     * 
     * @param entityType 实体类型
     * @param level 世界对象
     */
    public SitEntity(EntityType<? extends SitEntity> entityType, Level level) {
        super(entityType, level);
        this.noPhysics = true;
    }

    /**
     * 构造函数 - 创建坐姿实体
     * 
     * @param level 世界对象
     * @param pos 实体位置
     * @param forwardDirection 椅子朝向
     * @param chairPos 椅子位置
     */
    public SitEntity(Level level, Vec3 pos, int forwardDirection, BlockPos chairPos) {
        super(EntityRegister.SIT_ENTITY.get(), level);
        this.setPos(pos.x, pos.y, pos.z);
        this.entityData.set(DATA_FORWARD_DIRECTION, forwardDirection);
        this.entityData.set(DATA_CHAIR_POS, chairPos.asLong());
    }

    // ============================== 数据同步方法 ==============================
    
    /**
     * 定义同步数据
     * <p>
     * 初始化实体的同步数据，包括椅子朝向和椅子位置
     * </p>
     */
    @Override
    protected void defineSynchedData() {
        this.entityData.define(DATA_FORWARD_DIRECTION, 0);
        this.entityData.define(DATA_CHAIR_POS, BlockPos.ZERO.asLong());
    }

    // ============================== 数据保存方法 ==============================
    
    /**
     * 从NBT标签读取额外的实体数据
     * 
     * @param tag NBT标签
     */
    @Override
    protected void readAdditionalSaveData(CompoundTag tag) {
        this.entityData.set(DATA_FORWARD_DIRECTION, tag.getInt("ForwardDirection"));
        this.entityData.set(DATA_CHAIR_POS, tag.getLong("ChairPos"));
    }

    /**
     * 将额外的实体数据写入NBT标签
     * 
     * @param tag NBT标签
     */
    @Override
    protected void addAdditionalSaveData(CompoundTag tag) {
        tag.putInt("ForwardDirection", this.entityData.get(DATA_FORWARD_DIRECTION));
        tag.putLong("ChairPos", this.entityData.get(DATA_CHAIR_POS));
    }

    // ============================== Getter方法 ==============================
    
    /**
     * 获取椅子位置
     * 
     * @return 椅子位置
     */
    public BlockPos getChairPosition() {
        return BlockPos.of(this.entityData.get(DATA_CHAIR_POS));
    }

    // ============================== 实体更新方法 ==============================
    
    /**
     * 实体更新方法
     * <p>
     * 每tick调用一次，处理坐姿逻辑
     * </p>
     */
    @Override
    public void tick() {
        super.tick();

        // 如果没有乘客，销毁实体
        if (this.getPassengers().isEmpty() && !this.level().isClientSide) {
            this.discard();
            return;
        }

        // 检查椅子是否还存在
        if (!this.level().isClientSide) {
            BlockPos chairPos = this.getChairPosition();
            if (this.level().getBlockEntity(chairPos) == null &&
                    !(this.level().getBlockState(chairPos).getBlock() instanceof org.Yiran.timetale_re.block.decorate.Chair)) {
                // 椅子不存在了，让所有乘客下来
                for (Entity passenger : this.getPassengers()) {
                    if (passenger instanceof Player) {
                        passenger.stopRiding();
                    }
                }
                this.discard();
                return;
            }
        }

        // 检查乘客是否还在坐
        for (Entity passenger : this.getPassengers()) {
            if (passenger instanceof Player player) {
                // 如果玩家潜行，让他们站起来
                if (player.isShiftKeyDown()) {
                    player.stopRiding();
                    // 根据椅子朝向计算玩家应该被放置的位置
                    this.positionPlayerOnExit(player);
                    this.discard();
                    // 重置玩家的潜行状态
                    player.setShiftKeyDown(false);
                    return;
                }

                // 限制玩家的视角旋转角度，防止转向椅子背面
                this.limitPlayerRotation(player);
            }
        }

        // 保持实体位置不变
        this.setDeltaMovement(Vec3.ZERO);
    }

    // ============================== 位置计算方法 ==============================
    
    /**
     * 当玩家离开椅子时，计算玩家应该被放置的位置
     * 
     * @param player 离开椅子的玩家
     */
    private void positionPlayerOnExit(Player player) {
        BlockPos chairPos = this.getChairPosition();
        int forwardDirection = this.entityData.get(DATA_FORWARD_DIRECTION);

        // 定义椅子各个方向的偏移量
        BlockPos frontPos = this.getOffsetPos(chairPos, forwardDirection, 0); // 前
        BlockPos rightPos = this.getOffsetPos(chairPos, forwardDirection, 1); // 右
        BlockPos leftPos = this.getOffsetPos(chairPos, forwardDirection, 2);  // 左
        BlockPos upPos = chairPos.above();                                    // 上

        // 按照前→右→左→上的顺序检查可用位置
        if (this.isPositionFree(frontPos)) {
            player.teleportTo(frontPos.getX() + 0.5, frontPos.getY(), frontPos.getZ() + 0.5);
        } else if (this.isPositionFree(rightPos)) {
            player.teleportTo(rightPos.getX() + 0.5, rightPos.getY(), rightPos.getZ() + 0.5);
        } else if (this.isPositionFree(leftPos)) {
            player.teleportTo(leftPos.getX() + 0.5, leftPos.getY(), leftPos.getZ() + 0.5);
        } else {
            player.teleportTo(upPos.getX() + 0.5, upPos.getY(), upPos.getZ() + 0.5);
        }
    }

    /**
     * 根据椅子朝向和方向类型计算偏移位置
     * 
     * @param chairPos 椅子位置
     * @param forwardDirection 椅子朝向
     * @param directionType 0=前, 1=右, 2=左
     * @return 偏移后的位置
     */
    private BlockPos getOffsetPos(BlockPos chairPos, int forwardDirection, int directionType) {
        return switch (forwardDirection) {
            case 0 -> switch (directionType) { // 南
                case 0 -> chairPos.south();    // 前
                case 1 -> chairPos.west();     // 右
                case 2 -> chairPos.east();     // 左
                default -> chairPos;
            };
            case 1 -> switch (directionType) { // 西
                case 0 -> chairPos.west();     // 前
                case 1 -> chairPos.north();    // 右
                case 2 -> chairPos.south();    // 左
                default -> chairPos;
            };
            case 2 -> switch (directionType) { // 北
                case 0 -> chairPos.north();    // 前
                case 1 -> chairPos.east();     // 右
                case 2 -> chairPos.west();     // 左
                default -> chairPos;
            };
            case 3 -> switch (directionType) { // 东
                case 0 -> chairPos.east();     // 前
                case 1 -> chairPos.south();    // 右
                case 2 -> chairPos.north();    // 左
                default -> chairPos;
            };
            default -> chairPos;
        };
    }

    /**
     * 检查位置是否可以站立
     * 
     * @param pos 要检查的位置
     * @return 位置是否可用
     */
    private boolean isPositionFree(BlockPos pos) {
        return this.level().getBlockState(pos).isAir() &&
                this.level().getBlockState(pos.above()).isAir() &&
                this.level().getBlockState(pos.below()).isCollisionShapeFullBlock(this.level(), pos.below());
    }

    // ============================== 视角限制方法 ==============================
    
    /**
     * 限制玩家的旋转角度，防止转向椅子背面
     * 
     * @param player 坐在椅子上的玩家
     */
    private void limitPlayerRotation(Player player) {
        // 获取椅子的朝向
        int forwardDirection = this.entityData.get(DATA_FORWARD_DIRECTION);

        // 根据椅子朝向计算允许的视角范围
        float allowedYaw = this.getAllowedYaw(forwardDirection);

        // 获取玩家当前视角
        float playerYaw = player.getYRot();

        // 计算玩家相对于椅子正面的角度差
        float yawDiff = normalizeAngle(playerYaw - allowedYaw);

        // 如果超出限制范围，则调整视角
        if (yawDiff > 90.0f) {
            player.setYRot(allowedYaw + 90.0f);
        } else if (yawDiff < -90.0f) {
            player.setYRot(allowedYaw - 90.0f);
        }
    }

    /**
     * 根据椅子朝向获取允许的视角中心角度
     * 
     * @param forwardDirection 椅子朝向
     * @return 允许的视角中心角度
     */
    private float getAllowedYaw(int forwardDirection) {
        // 玩家坐在椅子上时，面朝的方向应该与椅子朝向相反
        // 因为玩家是坐在椅子的座位上，背靠靠背，面朝椅子的正面
        return switch (forwardDirection) {
            case 0 -> 0.0f;   // 椅子朝南，玩家面朝北
            case 1 -> 90.0f;  // 椅子朝西，玩家面朝东
            case 2 -> 180.0f; // 椅子朝北，玩家面朝南
            case 3 -> -90.0f; // 椅子朝东，玩家面朝西
            default -> 0.0f;
        };
    }

    /**
     * 将角度标准化到[-180, 180]范围
     * 
     * @param angle 输入角度
     * @return 标准化后的角度
     */
    private float normalizeAngle(float angle) {
        while (angle > 180.0f) angle -= 360.0f;
        while (angle < -180.0f) angle += 360.0f;
        return angle;
    }

    // ============================== 乘客管理方法 ==============================
    
    /**
     * 检查是否可以添加乘客
     * <p>
     * 只有当实体没有乘客且实体是LivingEntity时才能添加乘客
     * </p>
     * 
     * @param entity 要添加的实体
     * @return 是否可以添加乘客
     */
    @Override
    protected boolean canAddPassenger(@NotNull Entity entity) {
        return this.getPassengers().isEmpty() && entity instanceof LivingEntity;
    }

    /**
     * 获取乘客的乘坐偏移量
     * 
     * @return 乘客的乘坐偏移量
     */
    @Override
    public double getPassengersRidingOffset() {
        return 0.0;
    }

    // ============================== 网络同步方法 ==============================

    /**
     * 获取实体生成数据包
     *
     * @return 实体生成数据包
     */
    @Override
    public @NotNull Packet<ClientGamePacketListener> getAddEntityPacket() {
        return NetworkHooks.getEntitySpawningPacket(this);
    }

    /**
     * 写入实体数据到数据包
     * 
     * @param buffer 数据包缓冲区
     */
    @Override
    public void writeSpawnData(FriendlyByteBuf buffer) {
        buffer.writeDouble(this.getX());
        buffer.writeDouble(this.getY());
        buffer.writeDouble(this.getZ());
        buffer.writeLong(this.entityData.get(DATA_CHAIR_POS));
        buffer.writeInt(this.entityData.get(DATA_FORWARD_DIRECTION));
    }

    /**
     * 从数据包读取实体数据
     * 
     * @param buffer 数据包缓冲区
     */
    @Override
    public void readSpawnData(FriendlyByteBuf buffer) {
        this.setPos(buffer.readDouble(), buffer.readDouble(), buffer.readDouble());
        this.entityData.set(DATA_CHAIR_POS, buffer.readLong());
        this.entityData.set(DATA_FORWARD_DIRECTION, buffer.readInt());
    }
}